import numpy as np
import pandas as pd
from itertools import product
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
## starting parameters
# the first and last layers is air (non-lossy and set to 0 thickness)
# -- assuming we are interested in optical responses of the structure in the middle
thickness = np.array([0, 1000, 0])
ep = np.array([1, 4, 1])
# be consistent units e.g. if your wavelength is in nm, you film thickness should be in nm
wavelength = 500
n = np.sqrt(ep)
k = 2*np.pi*n/wavelength
# define angle of incidence
theta_inc = 0
theta_inc_rad = np.deg2rad(theta_inc)
# wavenumber in incidence material
k_inc = k[0]
# tagent component of wavenumber
k_tan = k_inc*np.sin(theta_inc_rad)
# angle of incidence in all layers
theta = np.arcsin(k_tan/k)
# propagating component of wavenumber in all layers
k_prop = np.sqrt(np.square(k) - np.square(k_tan))
# or -- k_prop = k*np.cos(theta)
# phase gain in all layers
delta = k*np.cos(theta)*thickness
def transfer_matrix_layer(delta_now, n1, n2, theta1, theta2):
'''
Calculate transfer-matrix of each layer.
'''
## TE component
# propagation matrix
prp_te = np.array( [[np.exp(-1j*delta_now), 0], [0, np.exp(1j*delta_now)]] )
# reflection matrix
r_te = (n1*np.cos(theta1) - n2*np.cos(theta2)) / (n1*np.cos(theta1) + n2*np.cos(theta2))
t_te = 2*n1*np.cos(theta1) / (n1*np.cos(theta1) + n2*np.cos(theta2))
rlc_te = 1 / t_te * np.array( [[1, r_te], [r_te, 1]] )
# combined matrix on that layer
m_te = np.matmul(prp_te, rlc_te)
## TM component
# propagation matrix -- same as TE
prp_tm = np.array( [[np.exp(-1j*delta_now), 0], [0, np.exp(1j*delta_now)]] )
# reflection matrix
r_tm = (n2*np.cos(theta1) - n1*np.cos(theta2)) / (n2*np.cos(theta1) + n1*np.cos(theta2))
t_tm = 2*n1*np.cos(theta1)/(n2*np.cos(theta1) + n1*np.cos(theta2))
rlc_tm = 1 / t_tm * np.array( [[1, r_tm], [r_tm, 1]])
# combined matrix on that layer
m_tm = np.matmul(prp_tm, rlc_tm)
return m_te, m_tm
def transfer_matrix(delta, n, theta):
'''
Calculate the combined transfer-matrix of wave propagation.
'''
# starting matrices
m_te_all = np.identity(2)
m_tm_all = np.identity(2)
# combine transfer matrix of all layers
for delta_now, n1, n2, theta1, theta2 in zip(delta[:-1], n[:-1], n[1:], theta[:-1], theta[1:]):
# transfer matrix of each layer
m_te, m_tm = transfer_matrix_layer(delta_now, n1, n2, theta1, theta2)
# multiply to the previous matrix
m_te_all = np.matmul(m_te_all, m_te)
m_tm_all = np.matmul(m_tm_all, m_tm)
return m_te_all, m_tm_all
def reflectance_transmitance(delta, n, theta):
'''
Get optical reflactance and transmittance (TE and TM).
Valid when first and last layers are non-lossy materials.
'''
# get m_te_all and m_tm_all
m_te_all, m_tm_all = transfer_matrix(delta, n, theta)
# TE reflection/transmission coefficients
reflect_te = m_te_all[1,0] / m_te_all[0,0]
transmt_te = 1 / m_te_all[0,0]
# TE reflectance and transmittance
r_power_te = np.abs(reflect_te)**2
t_power_te = np.abs(transmt_te)**2 * np.real( n[-1]*np.cos(theta[-1]) ) / np.real( n[0]*np.cos(theta[0]) )
# TM reflection/transmission coefficients
reflect_tm = m_tm_all[1,0] / m_tm_all[0,0]
transmt_tm = 1 / m_tm_all[0,0]
# TM reflectance and transmittance
r_power_tm = np.abs(reflect_tm)**2
t_power_tm = np.abs(transmt_tm)**2 * np.real( n[-1]*np.cos(theta[-1]) ) / np.real( n[0]*np.cos(theta[0]) )
return r_power_te, t_power_te, r_power_tm, t_power_tm
def get_params(thickness, n, wavelength, theta_inc):
'''
Get varying parameters due to changing wavelength and theta_inc.
'''
# calculate wavenumber in all layers
k = 2*np.pi*n/wavelength
# wavenumber in incidence material
k_inc = k[0]
# tagent component of wavenumber
theta_inc_rad = np.deg2rad(theta_inc)
k_tan = k_inc*np.sin(theta_inc_rad)
# angle of incidence in all layers
theta = np.arcsin(k_tan/k)
# propagating component of wavenumber in all layers
# k_prop = np.sqrt(np.square(k) - np.square(k_tan))
# or -- k_prop = k*np.cos(theta)
# phase gain in all layers
delta = k*np.cos(theta)*thickness
return delta, theta
def get_reflectance_transmittance(thickness, n, wavelength, theta_inc):
'''
Get optical reflactance and transmittance (TE, TM, and total).
'''
delta, theta = get_params(thickness, n, wavelength, theta_inc)
r_power_te, t_power_te, r_power_tm, t_power_tm = reflectance_transmitance(delta, n, theta)
return r_power_te, t_power_te, r_power_tm, t_power_tm, (r_power_te + r_power_tm)/2, (t_power_te + t_power_tm)/2
wavelength_range = range(400, 701, 1)
theta_inc_range = range(0, 90, 1)
excitation_df = pd.DataFrame(list(product(wavelength_range, theta_inc_range)),
columns=['wavelength', 'theta_inc']
)
response_df = pd.concat([excitation_df,
pd.DataFrame(excitation_df.apply(lambda x:
get_reflectance_transmittance(thickness, n, x.wavelength, x.theta_inc)
, axis=1
).tolist(),
columns = ['R_TE', 'T_TE', 'R_TM', 'T_TM', 'R_Total', 'T_Total']
)
],
axis=1
)
fig, axes = plt.subplots(3, 2, figsize=(15, 12), sharex=True, sharey=True)
for value, ax in zip(['R_TE', 'T_TE', 'R_TM', 'T_TM', 'R_Total', 'T_Total'], axes.flatten()):
sns.heatmap(pd.pivot_table(response_df,
values=value,
index='theta_inc',
columns='wavelength'
),
annot=False, ax=ax
)
ax.set_title(value)
ax.invert_yaxis()
fig.tight_layout()
import plotly.graph_objs as go
from ipywidgets import interactive, Label, HTML, HBox, VBox
from plotly.subplots import make_subplots
########################################
f_R_lambda = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.theta_inc==min(theta_inc_range), 'wavelength'],
y=response_df.loc[response_df.theta_inc==min(theta_inc_range), mode],
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
f_T_lambda = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.theta_inc==min(theta_inc_range), 'wavelength'],
y=response_df.loc[response_df.theta_inc==min(theta_inc_range), mode],
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
def update_RT_theta(theta_inc):
for selected_data, mode in zip(f_R_lambda.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = response_df.loc[response_df.theta_inc==theta_inc, mode]
for selected_data, mode in zip(f_T_lambda.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = response_df.loc[response_df.theta_inc==theta_inc, mode]
########################################
f_R_theta = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.wavelength==min(wavelength_range), 'theta_inc'],
y=response_df.loc[response_df.wavelength==min(wavelength_range), mode],
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
f_T_theta = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.wavelength==min(wavelength_range), 'theta_inc'],
y=response_df.loc[response_df.wavelength==min(wavelength_range), mode],
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
def update_RT_wavelength(wavelength):
for selected_data, mode in zip(f_R_theta.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = response_df.loc[response_df.wavelength==wavelength, mode]
for selected_data, mode in zip(f_T_theta.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = response_df.loc[response_df.wavelength==wavelength, mode]
########################################
theta_slider = interactive(update_RT_theta, theta_inc=(1, 90, 1))
lambda_slider = interactive(update_RT_wavelength, wavelength=(400, 700, 10))
hb1 = HBox((f_R_lambda, f_T_lambda))
vb1 = VBox((theta_slider, hb1))
vb1.layout.align_items = 'center'
hb2 = HBox((f_R_theta, f_T_theta))
vb2 = VBox((lambda_slider, hb2))
vb2.layout.align_items = 'center'
vb3 = VBox((vb1, vb2))
vb3
figs_R = []
figs_T = []
for mode in ['R_TE', 'R_TM', 'R_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig = go.FigureWidget(data=
go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode
),
layout=go.Layout(width=500, height=300, title=mode)
)
fig.update_layout(title={'x': 0.5, 'xanchor': 'center'})
figs_R = figs_R + [fig]
for mode in ['T_TE', 'T_TM', 'T_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig = go.FigureWidget(data=
go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode
),
layout=go.Layout(width=500, height=300, title=mode)
)
fig.update_layout(title={'x': 0.5, 'xanchor': 'center'})
figs_T = figs_T + [fig]
vb1 = VBox(figs_R)
vb2 = VBox(figs_T)
hb = HBox((vb1, vb2))
hb
########################################
fig = go.FigureWidget(make_subplots(rows=1, cols=2, subplot_titles=("Reflectance", "Transmittance")))
fig.update_xaxes(title_text='Wavelength (nm)')
fig.update_yaxes(title_text='Angle of Incidence (degree)')
fig.update_layout(height=400, width=800)
for mode in ['R_TE', 'R_TM', 'R_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode,
showscale=True
),
row=1, col=1
)
for mode in ['T_TE', 'T_TM', 'T_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode,
showscale=False
),
row=1, col=2
)
fig.update_layout(
updatemenus=[
go.layout.Updatemenu(
name='Optical Reflectance and Transmittance',
active=0,
x=0.5,
xanchor="center",
y=1.2,
yanchor="top",
buttons=list([
dict(label="Total",
method="update",
args=[{"visible": [False, False, True, False, False, True]},
# {"title": "TM Response"}
]),
dict(label="TE",
method="update",
args=[{"visible": [True, False, False, True, False, False]},
# {"title": "Total Response"}
]),
dict(label="TM",
method="update",
args=[{"visible": [False, True, False, False, True, False]},
# {"title": "TE Response"}
]),
]),
)
])
fig.show()
########################################
fig = go.FigureWidget(make_subplots(rows=1, cols=2, subplot_titles=("Reflectance", "Transmittance")))
for mode in ['R_TE', 'R_TM', 'R_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode
),
row=1, col=1
)
for mode in ['T_TE', 'T_TM', 'T_Total']:
heatmap = pd.pivot_table(response_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode,
),
row=1, col=2
)
fig.update_layout(
updatemenus=[
go.layout.Updatemenu(
name='Optical Reflectance and Transmittance',
active=0,
x=0.5,
xanchor="center",
y=1.2,
yanchor="top",
buttons=list([
dict(label="Total",
method="update",
args=[{"visible": [False, False, True, False, False, True]},
# {"title": "TM Response"}
]),
dict(label="TE",
method="update",
args=[{"visible": [True, False, False, True, False, False]},
# {"title": "Total Response"}
]),
dict(label="TM",
method="update",
args=[{"visible": [False, True, False, False, True, False]},
# {"title": "TE Response"}
]),
]),
)
])
########################################
f_R_lambda = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.theta_inc==min(theta_inc_range), 'wavelength'],
y=response_df.loc[response_df.theta_inc==min(theta_inc_range), mode],
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
f_T_lambda = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.theta_inc==min(theta_inc_range), 'wavelength'],
y=response_df.loc[response_df.theta_inc==min(theta_inc_range), mode],
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
def update_RT_theta(theta_inc):
for selected_data, mode in zip(f_R_lambda.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = response_df.loc[response_df.theta_inc==theta_inc, mode]
for selected_data, mode in zip(f_T_lambda.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = response_df.loc[response_df.theta_inc==theta_inc, mode]
########################################
f_R_theta = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.wavelength==min(wavelength_range), 'theta_inc'],
y=response_df.loc[response_df.wavelength==min(wavelength_range), mode],
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
f_T_theta = go.FigureWidget(
data=[go.Scatter(x=response_df.loc[response_df.wavelength==min(wavelength_range), 'theta_inc'],
y=response_df.loc[response_df.wavelength==min(wavelength_range), mode],
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500, legend_orientation='h', legend={'x':0,'y':1.1})
)
def update_RT_wavelength(wavelength):
for selected_data, mode in zip(f_R_theta.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = response_df.loc[response_df.wavelength==wavelength, mode]
for selected_data, mode in zip(f_T_theta.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = response_df.loc[response_df.wavelength==wavelength, mode]
#########################################
theta_slider = interactive(update_RT_theta, theta_inc=(1, 90, 1))
lambda_slider = interactive(update_RT_wavelength, wavelength=(400, 700, 10))
hb1 = HBox((f_R_lambda, f_T_lambda))
vb1 = VBox((theta_slider, hb1))
vb1.layout.align_items = 'center'
hb2 = HBox((f_R_theta, f_T_theta))
vb2 = VBox((lambda_slider, hb2))
vb2.layout.align_items = 'center'
vb3 = VBox((fig, vb1, vb2))
vb3
def get_n_and_thickness(n_real, n_imag, thickness):
'''
Get user inputs of refractive indices (real and imaginary) and layer thicknesses (nm).
'''
# convert to arguments to lists
n_real_list = [float(n.strip()) for n in n_real.split(';')]
n_imag_list = [float(n.strip()) for n in n_imag.split(';')]
thickness_list = [float(thickness.strip()) for thickness in thickness.split(';')]
# check number of input layers
if len(n_real_list) != len(n_imag_list):
raise Exception('Numbers of real and imaginary part of refractive indices are not equal.')
if len(n_real_list) != len(thickness_list):
raise Exception('Numbers of refractive indices and thicknesses are not equal.')
# get n and thickness
n = np.array([(n_real + 1J*n_imag) for n_real, n_imag in zip(n_real_list, n_imag_list)])
thickness = np.array(thickness_list)
# pad front and back with n=1 and thickness=0 (air)
n = np.pad(n, 1, 'constant', constant_values=1)
thickness = np.pad(thickness, 1, 'constant', constant_values=0)
return(n, thickness)
def get_excitation_conidtion_df():
'''
Excitation conditions to initialize dataframe for calculation.
Currently wavelength_range and theta_inc are fixed -- will allow user to update in future version.
'''
# currently wavelength_range and theta_inc are fixed -- will allow user to update in future version
wavelength_range = range(400, 701, 1)
theta_inc_range = range(0, 90, 1)
excitation_df = pd.DataFrame(list(product(wavelength_range, theta_inc_range)),
columns=['wavelength', 'theta_inc']
)
return excitation_df
def get_RT_all_conds(excitation_df, n, thickness):
'''
Calculate reflectance and transmittance for all excitation conditions and return RT dataframe.
'''
response_df = pd.DataFrame(excitation_df.apply(lambda x:
get_reflectance_transmittance(thickness,
n,
x.wavelength,
x.theta_inc
)
, axis=1
).tolist(),
columns = ['R_TE', 'T_TE', 'R_TM', 'T_TM', 'R_Total', 'T_Total']
)
RT_df = pd.concat([excitation_df, response_df], axis=1)
return RT_df
def plot_heatmaps(RT_df):
'''
Return heatmaps of reflectance and transmittance.
Also with option to select excitation mode to display (TE, TM, Total).
'''
fig = go.FigureWidget(make_subplots(rows=1, cols=2, subplot_titles=("Reflectance", "Transmittance")))
fig.update_xaxes(title_text='Wavelength (nm)')
fig.update_yaxes(title_text='Angle of Incidence (degree)')
fig.update_layout(height=400)#, width=800)
for mode in ['R_TE', 'R_TM', 'R_Total']:
heatmap = pd.pivot_table(RT_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode,
zmin=0, zmax=1,
showscale=False,
),
row=1, col=1
)
for mode in ['T_TE', 'T_TM', 'T_Total']:
heatmap = pd.pivot_table(RT_df, values=mode, index='theta_inc', columns='wavelength')
fig.add_trace(go.Heatmap(z=heatmap.values,x=heatmap.columns,y=heatmap.index,
colorscale='hot',
name=mode,
zmin=0, zmax=1,
showscale=True
),
row=1, col=2
)
fig.update_layout(
updatemenus=[
go.layout.Updatemenu(
name='Optical Reflectance and Transmittance',
active=0,
x=0.5,
xanchor="center",
y=1.2,
yanchor="top",
buttons=list([
dict(label="Total",
method="update",
args=[{"visible": [False, False, True, False, False, True]},
# {"title": "TM Response"}
]),
dict(label="TE",
method="update",
args=[{"visible": [True, False, False, True, False, False]},
# {"title": "Total Response"}
]),
dict(label="TM",
method="update",
args=[{"visible": [False, True, False, False, True, False]},
# {"title": "TE Response"}
]),
]),
)
])
#return fig
return fig
def update_layers_cal(n_real, n_imag, thickness):
'''
Get user specified information of thin film layers (refractive indeices and thicknesses).
Plot heatmaps of reflectance and transmittance.
Return RT dataframe for all excitation conditions.
'''
# get user specified information of thin film layers
n, thickness = get_n_and_thickness(n_real, n_imag, thickness)
# define excitation condition -- currently wavelength 400-700nm and theta_inc 0-89 deg
excitation_df = get_excitation_conidtion_df()
# get RT dataframe
RT_df = get_RT_all_conds(excitation_df, n, thickness)
# plot the RT heatmaps
plot_heatmaps(RT_df).show()
# return RT dataframe in form of update_layers.result
return RT_df
# test getting user input and plotting RT heatmaps
update_layers = interactive(update_layers_cal,
{'manual': True, 'manual_name': 'Update layers'},
n_real='Real{n}',
n_imag='Imag{n}',
thickness='Thickness (nm)'
)
update_layers
update_layers.result
# UI to update thin film layers
update_layers = interactive(update_layers_cal,
{'manual': True, 'manual_name': 'Update layers'},
n_real='Real{n}',
n_imag='Imag{n}',
thickness='Thickness (nm)'
)
# fixed wavelength for now
# need to also update get_excitation_conidtion_df() when user type in these two parameters
wavelength_range = range(400, 701, 1)
theta_inc_range = range(0, 90, 1)
# set initial plots before user provides information of thin film layers
if update_layers.result is None:
#### RT across wavelength range
# Reflectance across range of wavelength -- initially all 0
f_R_lambda = go.FigureWidget(
data=[go.Scatter(x=np.array(wavelength_range),
y=np.zeros(len(wavelength_range)),
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500,
legend_orientation='h', legend={'x':0,'y':1.1},
xaxis_title='Wavelength (nm)',
yaxis_title='Reflectance (a.u.)',
yaxis={'range':[-0.1, 1.1]}
)
)
# Transmittance across range of wavelength -- initially all 1
f_T_lambda = go.FigureWidget(
data=[go.Scatter(x=np.array(wavelength_range),
y=np.ones(len(wavelength_range)),
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500,
legend_orientation='h',
legend={'x':0,'y':1.1},
xaxis_title='Wavelength (nm)',
yaxis_title='Transmittance (a.u.)',
yaxis={'range':[-0.1, 1.1]}
)
)
#### RT across theta_inc range
# Reflectance across range of angle of incidencec -- initially all 0
f_R_theta = go.FigureWidget(
data=[go.Scatter(x=np.array(theta_inc_range),
y=np.zeros(len(theta_inc_range)),
name=mode
) for mode in ['R_TE', 'R_TM', 'R_Total']
],
layout=go.Layout(width=500,
legend_orientation='h', legend={'x':0,'y':1.1},
xaxis_title='Angle of Incidence (degree)',
yaxis_title='Reflectance (a.u.)',
yaxis={'range':[-0.1, 1.1]}
)
)
# Transmittance across range of angle of incidencec -- initially all 1
f_T_theta = go.FigureWidget(
data=[go.Scatter(x=np.array(theta_inc_range),
y=np.ones(len(theta_inc_range)),
name=mode
) for mode in ['T_TE', 'T_TM', 'T_Total']
],
layout=go.Layout(width=500,
legend_orientation='h', legend={'x':0,'y':1.1},
xaxis_title='Angle of Incidence (degree)',
yaxis_title='Transmittance (a.u.)',
yaxis={'range':[-0.1, 1.1]}
)
)
########################################
# function to plot RT at different theta_inc
def update_RT_theta(theta_inc):
if update_layers.result is None:
pass
else:
RT_df = update_layers.result
for selected_data, mode in zip(f_R_lambda.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = RT_df.loc[RT_df.theta_inc==theta_inc, mode]
for selected_data, mode in zip(f_T_lambda.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = RT_df.loc[RT_df.theta_inc==theta_inc, mode]
# UI to update theta_inc
theta_slider = interactive(update_RT_theta, theta_inc=(1, 90, 1))
########################################
# function to plot RT at different wavelength
def update_RT_wavelength(wavelength):
if update_layers.result is None:
pass
else:
RT_df = update_layers.result
for selected_data, mode in zip(f_R_theta.data, ['R_TE', 'R_TM', 'R_Total']):
selected_data.y = RT_df.loc[RT_df.wavelength==wavelength, mode]
for selected_data, mode in zip(f_T_theta.data, ['T_TE', 'T_TM', 'T_Total']):
selected_data.y = RT_df.loc[RT_df.wavelength==wavelength, mode]
# UI to update lambda
lambda_slider = interactive(update_RT_wavelength, wavelength=(400, 700, 10))
########################################
# header messages
header_text = 'Interactive heatmaps and plots of optical reflectance and transmittance through thin films using transfer-matrix calculation.'
header = HTML(value='<{size}>{text}</{size}>'.format(text=header_text, size='h2'))
description_text = 'Assume light propagates from <u><b>air</b></u> to <b>specified thin film layers</b>, then exits to <u><b>air</b></u>.'
description = HTML(value='<{size}>{text}</{size}>'.format(text=description_text, size='h3'))
# description to update thin film layers
update_layers_inst = 'Update layers and perform calculation. Note that it could take a while.'
update_layers_note = HTML(value='<{size}>{text}</{size}>'.format(text=update_layers_inst, size='h3'))
update_layers_n_real = 'List real part of refractive indices of materials from the front to the back of a thin film stack separated by <b>;</b> (e.g. 1.5; 1.33; 1.5).'
update_layers_n_imag = 'List imaginary part of refractive indices of materials separated by <b>;</b> (e.g. 0.1; 0; 0.05, positive and negative values for absorber and amplifier, respectively).'
update_layers_thickness = 'List thickness of materials in <b>nanometer</b> separated by <b>;</b> (e.g. 500; 800; 500).'
update_layers_text = f'{update_layers_n_real}<br>{update_layers_n_imag}<br>{update_layers_thickness}'
update_layers_html = HTML(value=update_layers_text)
# RT across range of wavelength at difference theta_inc
theta_slide_text = 'Move <b>theta_inc slider</b> to see reflectance and transmittance at different angle of incidence.'
theta_slide_html = HTML(value='<{size}>{text}</{size}>'.format(text=theta_slide_text, size='h3'))
hb1 = HBox((f_R_lambda, f_T_lambda))
vb1 = VBox((theta_slide_html, theta_slider, hb1))
# RT across range of theta_inc at difference wavelength
lambda_slide_text = 'Move <b>wavelength slider</b> to see reflectance and transmittance at different wavelength.'
lambda_slide_html = HTML(value='<{size}>{text}</{size}>'.format(text=lambda_slide_text, size='h3'))
hb2 = HBox((f_R_theta, f_T_theta))
vb2 = VBox((lambda_slide_html, lambda_slider, hb2))
output = VBox((header,
description,
Label('#'*100),
update_layers_note,
update_layers_html,
update_layers,
Label('#'*100),
vb1,
Label('#'*100),
vb2
)
)
output